;
(function () {
    var o = {},
        oStage = document.getElementById('stage'), // canvas对象
        stage = new createjs.Stage('stage'), // 舞台
        infoBox = new createjs.Container(), // 步骤说明
        fnStack = new createjs.Container(), // 函数调用栈容器
        macroTask = new createjs.Container(), // 宏任务容器
        microTask = new createjs.Container(), // 微任务容器
        btnGroup = new createjs.Container(), // 操作按钮容器
        stackW = 150,
        stackH = 600,
        CEW = stackW - 20,
        CEH = 30,
        btnW = 80,
        btnH = 30,
        queueW = 500,
        queueH = 50,
        taskW = 115,
        taskH = CEH,
        step = -1

    o.step = {}

    // 初始化步骤说明
    o.initInfoBox = function () {
        infoBox.x = (window.innerWidth - window.innerWidth / 3 * 2) / 2
        infoBox.y = -2
    }

    // 创建步骤信息
    o.createInfo = function () {
        var infoBoxBounds
        var info = new createjs.Text(o.step.data[step].info, '20px yahei', '#fff')
        info.x = 30
        info.y = 30
        infoBox.addChild(info)
    }

    // 初始化函数调用栈
    o.initFnStack = function () {
        var title = new createjs.Text('函数调用栈', '20px yahei', '#fff'),
            stack = new createjs.Shape(),
            titleBounds = title.getBounds()

        fnStack.addChild(title)
        fnStack.addChild(stack)
        stack.graphics.setStrokeStyle(1).beginStroke('#fff').drawRoundRect(0, 0, stackW, stackH, 5)
        fnStack.x = (window.innerWidth / 2 - stackW) / 2 + window.innerWidth / 2 + 100
        fnStack.y = (window.innerHeight - stackH) / 2
        title.x = (stackW - titleBounds.width) / 2
        title.y = -titleBounds.height - 15
    }

    // 创建执行上下文
    o.createCE = function (name) {
        if (fnStack.getChildByName(name)) {
            return
        }
        var CE = new createjs.Container()
        var title = new createjs.Text(name, '18px yahei', '#fff'),
            box = new createjs.Shape(),
            titleBounds = title.getBounds()

        fnStack.addChild(CE)
        CE.addChild(title)
        CE.addChild(box)
        CE.mask = new createjs.Shape(new createjs.Graphics().beginFill("#000").drawRect(0, 0, stackW, stackH))
        box.graphics.setStrokeStyle(1).beginStroke('#fff').drawRoundRect(0, 0, CEW, CEH, 5)
        title.x = (CEW - titleBounds.width) / 2
        title.y = (CEH - titleBounds.height) / 2
        CE.x = (stackW - CEW) / 2
        CE.y = stackH - (CEH + 10) * (fnStack.children.length - 2)
        CE.name = name
        o.reInitCEPos()
    }

    // 重新计算执行上下文的位置
    o.reInitCEPos = function () {
        fnStack.children.forEach(function (child, index) {
            if (index > 1) {
                child.y = stackH - (CEH + 10) * (index - 1)
            }
        })
    }

    // 初始化按钮组
    o.initBtnGroup = function () {
        btnGroup.x = window.innerWidth - btnW - 100
        btnGroup.y = 100
    }

    // 创建按钮
    o.createBtn = function (name, clickEvent) {
        var btn = new createjs.Container()
        var title = new createjs.Text(name, '18px yahei', '#fff'),
            box = new createjs.Shape(),
            titleBounds = title.getBounds()
        btnGroup.addChild(btn)
        btn.addChild(box)
        btn.addChild(title)
        box.graphics.setStrokeStyle(1).beginFill('#282c34').beginStroke('#fff').drawRoundRect(0, 0, btnW, btnH, 5)
        title.x = (btnW - titleBounds.width) / 2
        title.y = (btnH - titleBounds.height) / 2
        btn.y = (btnGroup.children.length - 2) * (btnH + 20)
        btn.cursor = 'pointer'
        btn.name = name

        if (typeof clickEvent === 'function') {
            btn.addEventListener('click', clickEvent)
        }

        btn.addEventListener('mouseover', function (e) {
            box.graphics.beginFill('#2795fc').beginStroke('#2795fc').drawRoundRect(0, 0, btnW, btnH, 5)
            o.draw()
        })

        btn.addEventListener('mouseout', function (e) {
            box.graphics.beginFill('#282c34').beginStroke('#fff').drawRoundRect(0, 0, btnW, btnH, 5)
            o.draw()
        })
    }

    // 初始化宏任务
    o.initMacroTask = function () {
        var title = new createjs.Text('宏任务', '20px yahei', '#fff'),
            titleBounds = title.getBounds()

        macroTask.addChild(title)
        title.x = (queueW - titleBounds.width) / 2
        title.y = -titleBounds.height - 15
        macroTask.x = fnStack.x - queueW - 50
        macroTask.y = fnStack.y
    }

    // 初始化微任务
    o.initMicroTask = function () {
        var title = new createjs.Text('微任务', '20px yahei', '#fff'),
            titleBounds = title.getBounds()

        microTask.addChild(title)
        title.x = (queueW - titleBounds.width) / 2
        title.y = -titleBounds.height - 15
        microTask.x = fnStack.x - queueW - 50
        microTask.y = macroTask.y + macroTask.getBounds().height + 150
    }

    // 重新计算微任务的位置
    o.reInitMicroTaskPos = function () {
        microTask.y = macroTask.y + macroTask.getBounds().height + 150
    }

    // 创建队列
    o.createQueue = function (name, taskType) {
        if (taskType.getChildByName(name)) {
            return
        }
        var queue = new createjs.Container(),
            title = new createjs.Text(name, '18px yahei', '#fff'),
            titleBounds = title.getBounds(),
            arrow = new createjs.Container(),
            lineT = new createjs.Shape(),
            lineB = new createjs.Shape(),
            diagonal1 = new createjs.Shape(),
            diagonal2 = new createjs.Shape()

        taskType.addChild(queue)
        arrow.addChild(lineT)
        arrow.addChild(lineB)
        arrow.addChild(diagonal1)
        arrow.addChild(diagonal2)
        queue.addChild(title)
        queue.addChild(arrow)

        queue.mask = new createjs.Shape(new createjs.Graphics().beginFill("#000").drawRect(0, -10, queueW + 25, 3000))

        title.y = -10
        lineT.graphics.beginStroke('#fff').moveTo(0, titleBounds.height).lineTo(queueW, titleBounds.height)
        lineB.graphics.beginStroke('#fff').moveTo(0, titleBounds.height + queueH).lineTo(queueW, titleBounds.height + queueH)
        diagonal1.graphics.beginStroke('#fff').moveTo(queueW + 25, titleBounds.height + queueH / 2).lineTo(queueW + 80, titleBounds.height + queueH / 2)
        diagonal2.graphics.beginStroke('#fff').moveTo(queueW + 25, titleBounds.height + queueH / 2).lineTo(queueW + 80, titleBounds.height + queueH / 2)
        diagonal1.set({
            x: queueW + 25,
            y: titleBounds.height + queueH / 2,
            rotation: -135,
            regX: queueW + 25,
            regY: titleBounds.height + queueH / 2
        })
        diagonal2.set({
            x: queueW + 25,
            y: titleBounds.height + queueH / 2,
            rotation: 135,
            regX: queueW + 25,
            regY: titleBounds.height + queueH / 2
        })

        queue.y = (taskType.children.length - 2) * (queue.getBounds().height + 100)
        queue.name = name
        o.reInitMicroTaskPos()
    }

    // 重新初始化队列位置
    o.reInitQueuePos = function (queue, taskType) {
        taskType.children.forEach(function (child, index) {
            if (index > 0) {
                child.y = (index - 1) * (queue.getBounds().height + 100)
            }
        })
    }

    // 创建具体任务
    o.createTask = function (name, queueName, taskType) {
        var task = new createjs.Container()
        var title = new createjs.Text(name, '18px yahei', '#fff'),
            box = new createjs.Shape(),
            titleBounds = title.getBounds(),
            queue = taskType ? taskType.getChildByName(queueName) : null

        if (!queue) {
            return;
        }

        if (queue.getChildByName(name)) {
            return
        }

        queue.addChild(task)
        task.addChild(box)
        task.addChild(title)
        box.graphics.setStrokeStyle(1).beginFill('#282c34').beginStroke('#fff').drawRoundRect(0, 0, taskW, taskH, 5)
        title.x = (taskW - titleBounds.width) / 2
        title.y = (taskH - titleBounds.height) / 2
        task.name = name
        task.x = (queue.children.length - 3) * (taskW + 15) + 6
        task.y = 31
    }

    // 重新初始化任务位置
    o.reInitTaskPos = function (queue) {
        queue.children.forEach(function (child, index) {
            if (index > 1) {
                child.x = (queue.children.length - index - 1) * (taskW + 15) + 6
            }
        })
    }

    // 执行步骤
    o.doStep = function () {
        var curr = o.step.data[step]

        if (!curr) {
            return
        }

        o.clear()

        if (curr.info) {
            o.createInfo()
        }

        if (curr.queue && curr.queue.length) {
            curr.queue.forEach(function (queue) {
                if (queue.type === 'macro') {
                    o.createQueue(queue.name, macroTask)
                } else {
                    o.createQueue(queue.name, microTask)
                }
            })
        }

        if (curr.CE && curr.CE.length) {
            curr.CE.forEach(function (CE) {
                o.createCE(CE)
            })
        }

        if (curr.task && curr.task.length) {
            curr.task.forEach(function (task) {
                if (task.type === 'macro') {
                    o.createTask(task.name, task.queue, macroTask)
                } else {
                    o.createTask(task.name, task.queue, microTask)
                }
            })
        }

        o.draw()
    }

    o.initCodeImg = function () {
        if (o.step.code) {
            var img = new Image()
            img.src = o.step.code
            img.onload = function () {
                var code = new createjs.Bitmap(o.step.code),
                    codeBounds = code.getBounds()

                stage.addChild(code)
                code.x = 50
                code.y = (window.innerHeight - codeBounds.height) / 2

                if (window.innerWidth / 2 < codeBounds.width + code.x) {
                    var scale = window.innerWidth / 2 / (codeBounds.width + code.x)
                    code.set({
                        scaleX: scale,
                        scaleY: scale
                    })
                } else {
                    code.set({
                        scaleX: 1,
                        scaleY: 1
                    })
                }
                o.draw()
            }
        }
    }

    // 清除当前的队列、任务和函数调用栈的状态
    o.clear = function () {
        while (fnStack.children.length > 2) {
            fnStack.children.pop()
        }

        while (macroTask.children.length > 1) {
            macroTask.children.pop()
        }

        while (microTask.children.length > 1) {
            microTask.children.pop()
        }

        infoBox.children.pop()
    }

    // 绘制
    o.draw = function () {
        stage.update()
    }

    // 初始化
    o.init = function (data) {
        o.step = data
        oStage.setAttribute('width', window.innerWidth)
        oStage.setAttribute('height', window.innerHeight)
        stage.enableMouseOver()
        o.initCodeImg()
        stage.addChild(infoBox)
        stage.addChild(fnStack)
        stage.addChild(macroTask)
        stage.addChild(microTask)
        stage.addChild(btnGroup)
        o.initInfoBox()
        o.initFnStack()
        o.initMacroTask()
        o.initMicroTask()
        o.initBtnGroup()
        o.createBtn('上一步', function () {
            if (step - 1 >= 0) {
                step--
                o.doStep()
            }
        })
        o.createBtn('下一步', function () {
            if (o.step.data[step + 1]) {
                step++;
                o.doStep()
            }
        })
        o.draw()
    }

    window.eventLoop = o.init
})();